#ifndef AUDITION_HPP
#define AUDITION_HPP

#include <windows.h>
#include <mmsystem.h>

namespace audio
{
	class format
	{
	public:
		WAVEFORMATEX format_;
		format(WORD channels, DWORD samples, WORD bits)
		{
			ZeroMemory(&format_,sizeof(WAVEFORMATEX));
			format_.wFormatTag = WAVE_FORMAT_PCM;
			format_.nChannels = channels;
			format_.nSamplesPerSec = samples;
			format_.wBitsPerSample = bits;
			format_.nBlockAlign = format_.nChannels * format_.wBitsPerSample / 8;
			format_.nAvgBytesPerSec = format_.nSamplesPerSec * format_.nBlockAlign;
			format_.cbSize = sizeof(WAVEFORMATEX);
		}
	};

	class header
	{
	private:
		HGLOBAL memory;
	public:
		WAVEHDR header_;
		header(format& wf, DWORD seconds)
		{
			ZeroMemory(&header_,sizeof(WAVEHDR));
			memory = GlobalAlloc(GMEM_FIXED, wf.format_.nAvgBytesPerSec*seconds);
			header_.lpData = (LPSTR)memory;
			header_.dwBufferLength = wf.format_.nAvgBytesPerSec*seconds;
		}
		LPSTR begin() const
		{
			return header_.lpData;
		}
		LPSTR end() const
		{
			return (header_.lpData+header_.dwBufferLength);
		}
		DWORD size() const
		{
			return header_.dwBufferLength;
		}
		~header()
		{
			GlobalFree(memory);
		}
	};

	class in
	{
	private:
		HANDLE hEvent;
		HWAVEIN hDevice;
	public:
		in(format& wf, UINT deviceID)
		{
			hEvent = CreateEvent(0,0,1,0);
			waveInOpen(&hDevice, deviceID, &wf.format_, (DWORD_PTR)hEvent, NULL, CALLBACK_EVENT | WAVE_ALLOWSYNC);
		}
		void record(header& h) const
		{
			waveInPrepareHeader(hDevice, &h.header_, sizeof(WAVEHDR));
			waveInAddBuffer(hDevice,&h.header_,sizeof(WAVEHDR));
			ResetEvent(hEvent);
			waveInStart(hDevice);
			WaitForSingleObject(hEvent, INFINITE);
			waveInUnprepareHeader(hDevice, &h.header_,sizeof(WAVEHDR));
		}
		~in()
		{
			waveInClose(hDevice);
			CloseHandle(hEvent);
		}

	};

	class out
	{
	private:
		HANDLE hEvent;
		HWAVEOUT hDevice;
	public:
		out(format& wf, UINT deviceID)
		{
			hEvent = CreateEvent(0,0,1,0);
			waveOutOpen(&hDevice, deviceID, &wf.format_, (DWORD_PTR)hEvent, NULL, CALLBACK_EVENT | WAVE_ALLOWSYNC);
		}
		~out()
		{
			waveOutClose(hDevice);
			CloseHandle(hEvent);
		}
		void play(header& h) const
		{
			waveOutPrepareHeader(hDevice, &h.header_, sizeof(WAVEHDR));
			waveOutWrite(hDevice, &h.header_, sizeof(WAVEHDR));
			ResetEvent(hEvent);
			WaitForSingleObject(hEvent, INFINITE);
			waveOutUnprepareHeader(hDevice, &h.header_, sizeof(WAVEHDR));
		}
	};

}

#endif